package ocean.inventoryprotection.common.event;

import net.minecraft.entity.Entity;
import net.minecraft.entity.MobEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ShieldItem;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.NonNullList;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.event.entity.living.LivingExperienceDropEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import ocean.InventoryProtection;
import ocean.inventoryprotection.common.ModItems;
import ocean.inventoryprotection.common.config.ApplicationConfig;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 监听
 *
 * @author zhang
 * @since 2023/1/15
 */
@Mod.EventBusSubscriber(modid = InventoryProtection.MODID)
public class ModEvents {

    private static final String APPLICATION_NAME = "InventoryProtection_playerInventoryDrops";
    private static final String DROP_SIZE = "dropSize";
    private static final String DROP_PREFIX = "dropPrefix";
    private static final String DROP_LEVEL_POINT = "dropLevelPoint";

    @SubscribeEvent
    public static void onLivingDropsEvent(LivingDropsEvent event) {
        if (event.getEntity() instanceof PlayerEntity && container(ModItems.PROTECTOR.get(), event.getDrops())) {
            List<ItemEntity> drops = event.getDrops().stream().map(res -> {
                if (!res.getItem().isEmpty()) {
                    return res;
                }
                return null;
            }).filter(Objects::nonNull).collect(Collectors.toList());
            if (drops.size() > 0) {
                event.getDrops().removeAll(drops);
                CompoundNBT inventorySize = new CompoundNBT();
                for (int i = 0; i < drops.size(); i++) {
                    ItemStack stack = drops.get(i).getItem();
                    CompoundNBT tagItem = stack.save(new CompoundNBT());
                    inventorySize.put(DROP_PREFIX + i, tagItem);
                }
                CompoundNBT data = event.getEntity().getPersistentData();
                if (!data.contains(PlayerEntity.PERSISTED_NBT_TAG)) {
                    data.put(PlayerEntity.PERSISTED_NBT_TAG, new CompoundNBT());
                }
                inventorySize.putInt(DROP_SIZE, drops.size());
                inventorySize.putInt(DROP_LEVEL_POINT, ((PlayerEntity) event.getEntity()).totalExperience);
                CompoundNBT persist = data.getCompound(PlayerEntity.PERSISTED_NBT_TAG);
                persist.put(APPLICATION_NAME, inventorySize);
            }
        }
    }

    @SubscribeEvent
    public static void onLivingExperienceDropEvent(LivingExperienceDropEvent event) {
        if (ApplicationConfig.LEVEL.get() && event.getEntity().getPersistentData().contains(PlayerEntity.PERSISTED_NBT_TAG)) {
            event.setCanceled(true);
        }
    }

    @SubscribeEvent
    public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
        Entity entity = event.getEntity();
        PlayerEntity player = (PlayerEntity) entity;
        CompoundNBT data = player.getPersistentData();
        if (data.contains(PlayerEntity.PERSISTED_NBT_TAG)) {
            CompoundNBT inventoryIndex = data.getCompound(PlayerEntity.PERSISTED_NBT_TAG);
            CompoundNBT allDrops = inventoryIndex.getCompound(APPLICATION_NAME);
            int count = allDrops.getInt(DROP_SIZE);
            if (count > 0) {
                for (int i = 0; i < count; i++) {
                    CompoundNBT item = allDrops.getCompound(DROP_PREFIX + i);
                    ItemStack stack = ItemStack.of(item);
                    if (!stack.isEmpty()) {
                        ItemStack copy = stack.copy();
                        if (copy.getItem() == ModItems.PROTECTOR.get()) {
                            copy.shrink(1);
                        }
                        inventoryReset(player, copy);
                    }
                }
                if (ApplicationConfig.LEVEL.get()) {
                    player.giveExperiencePoints(allDrops.getInt(DROP_LEVEL_POINT));
                }
                player.sendMessage(new StringTextComponent(ApplicationConfig.MESSAGE.get()), player.getUUID());
            }
            inventoryIndex.remove(APPLICATION_NAME);
        }
    }

    private static void inventoryReset(PlayerEntity player, ItemStack copy) {
        if (copy.getItem() instanceof ArmorItem) {
            EquipmentSlotType equipmentslot = MobEntity.getEquipmentSlotForItem(copy);
            if (container(player.inventory.armor, copy)) {
                addInventory(player, copy);
            } else {
                player.setItemSlot(equipmentslot, copy.copy());
            }
        } else if (copy.getItem() instanceof ShieldItem) {
            EquipmentSlotType equipmentslot = MobEntity.getEquipmentSlotForItem(copy);
            if (container(player.inventory.offhand, copy)) {
                addInventory(player, copy);
            } else {
                player.setItemSlot(equipmentslot, copy.copy());
            }
        } else {
            addInventory(player, copy);
        }
    }

    public static void addInventory(PlayerEntity player, ItemStack itemStack) {
        if (!player.inventory.add(itemStack)) {
            player.drop(itemStack, true);
        }
    }

    public static boolean container(Item item, Collection<ItemEntity> itemEntities) {
        for (ItemEntity itemEntity : itemEntities) {
            if (itemEntity.getItem().getItem() == item) {
                return true;
            }
        }
        return false;
    }

    public static boolean container(NonNullList<ItemStack> itemStacks, ItemStack itemStack) {
        for (ItemStack stack : itemStacks) {
            if (stack.sameItem(itemStack)) {
                return true;
            }
        }
        return false;
    }
}
